/*      Copyright 2010 Jesus McCloud <bernd.pruenster@gmail.com>
 *      
 *      This program is free software; you can redistribute it and/or modify
 *      it under the terms of the GNU General Public License as published by
 *      the Free Software Foundation; either version 3 of the License, or
 *      (at your option) any later version.
 *      
 *      This program is distributed in the hope that it will be useful,
 *      but WITHOUT ANY WARRANTY; without even the implied warranty of
 *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *      GNU General Public License for more details.
 *      
 *      You should have received a copy of the GNU General Public License
 *      along with this program; if not, write to the Free Software
 *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 *      MA 02110-1301, USA.
 * 
 *      This parser does all the actual work. it still needs lotsa love
 */

//compile with --pkg gtksourceview-2.0 --pkg libxml-2.0 --pkg gtk+-2.0  --pkg gmodule-2.0 --pkg gee-1.0

using Gtk;

namespace jmc{
public class Parser : GLib.Object{
  
  const string WIDGET_DECLARATION = "/*Widget Declaration*/";
  const string CONSTRUCTOR = "/*Default Constructor*/";
  const string WIDGET_CREATION = "/*Widget Creation*/";
  const string CREATE_OBJECT_AND_RUN = "/*Create new Object of Class*/";
  const string CONNECT_DESTROY = "/*Connect Destroy*/";
  const string SIGNAL_HANDLER = "/*Signal Handlers*/";
  
  //variable declaration  
  public bool file_opened;
  bool members=true;
  public Gee.LinkedList<string> widgets{get; set;}
  public Gee.HashSet<string> handlers{get; set;}
  public Gee.LinkedList<string> code_template{get; set;}
  private int index;
  public string class_name;
  public string variable_name;
  public string klass;
  public string file_name;
  private bool namespace_set=false;
  StringBuilder text;
  
  
  public Parser(){  
    //varaible initailization
    file_opened=false;
    namespace_set=false;
   
    code_template= new Gee.LinkedList<string>(null);
    text = new StringBuilder();
    widgets = new Gee.LinkedList<string>(null);
    handlers = new Gee.HashSet<string>();
    index=0;
    klass ="Klass";
    class_name="class_name";
    variable_name= "varaible_name";
    file_name="file_name";
   // create_ui();
    
    ///////
       
    /////////
    
  }

  public void init_template(string template_file_name){
    try{
      string file_tmp;
      FileUtils.get_contents(template_file_name,out file_tmp,null);
      string[] template_lines= file_tmp.split("\n");
      code_template.clear();
      for(int i=0;i<template_lines.length;i++){
        code_template.add(template_lines[i]);
      }
    }catch(GLib.Error e){
      stderr.printf("Template not present");
    }
  }


 
  

  public int[] init_widget_indices(Gtk.ListStore widget_table){
    int[] temp_ind={};
    int[] result_indices;
    int number_indices=0;
    TreeIter iter;
    bool t=false;
    for(int i=0;i<widgets.size;i++){
      widget_table.get_iter (out iter, new TreePath.from_indices(i));
      widget_table.get(iter,0, out t);
      if(t){
        number_indices++;
        temp_ind+=i;
      }
    }
    result_indices=new int[number_indices];
    for(int i=0;i<number_indices;i++)
      result_indices[i] =temp_ind[i];
    return result_indices;
  }
  
  public void open_file(string file_name){
      index=0;
      widgets.clear();
    Xml.Parser.init();
    parse_file(file_name);
    Xml.Parser.cleanup();
    file_opened=true;
  }
  
  private void parse_file (string path) {
    Xml.Doc* doc = Xml.Parser.parse_file (path);
    if (doc == null) {
      stderr.printf ("File %s not found or permissions missing", path);
      return;
    }

    // Get the root node.
    Xml.Node* root = doc->get_root_element ();
    if (root == null) {
      // Free the document manually before returning
      delete doc;
      stderr.printf ("The xml file '%s' is empty", path);
      return;
    }
    
    parse_node (root);
    delete doc;
  }
  
  private void parse_node (Xml.Node* node) {
    // Loop over the passed node's children
    for (Xml.Node* iter = node->children; iter != null; iter = iter->next) {
      // Spaces between tags are also nodes, discard them
      if (iter->type != Xml.ElementType.ELEMENT_NODE) {
        continue;
      }
      // Parse the node's properties (attributes) ...
      parse_properties (iter);
      // Followed by its children nodes
      parse_node (iter);
    }
  }
  
  private void parse_properties (Xml.Node* node) {
    // Loop over the passed node's properties (attributes)
    for (Xml.Attr* prop = node->properties; prop != null; prop = prop->next) {
      string attr_name = prop->name;
      int oldindex=index;
      // Notice the ->children which points to a Node*
      // (Attr doesn't feature content)
      string attr_content = prop->children->content;
      if(node->name=="signal"){
        if(oldindex==index)
        {
          class_name+=attr_content+";";
        }
        attr_name=index.to_string()+" signal "+attr_name;
      }else{
        if(attr_name=="class"||attr_name=="id"){
          if(attr_name=="class"){
            index++;
            if(oldindex!=index)
            {
              if(oldindex!=0)
                widgets.add(class_name);
              class_name=attr_content.replace("Gtk","Gtk.")+";";
            }
          }else{
            class_name+=attr_content+";";
          }
        }
      }
    }
  }
  
  public void create_body_head(string name_namespace, string name_class, string xml_file){
    
   // code template.insert(0,("  const string GLADE = \"%s\";\n".printf(file_name)));
    code_template.insert(0, "  const string GLADE =\"%s\";\n".printf(xml_file));
    if(name_class!="") this.klass= name_class;
    code_template.insert(0,"public class %s : GLib.Object{".printf(klass));
    
    
    if(name_namespace!=""){
      code_template.insert(0,"namespace %s{".printf(name_namespace));
      namespace_set=true;
    }else namespace_set =false;
  }
  
  
  public void create_body_variables(string main_window_name, Gtk.ListStore widget_table){
    int wid_i =code_template.index_of(WIDGET_DECLARATION);
    this.members=false;
    if(wid_i!=-1){
      this.members=true;
      int[] widget_indices= init_widget_indices(widget_table);
      for(int i=0;i<widget_indices.length;i++){
        string[] variable=(widgets.get(widget_indices[i])).split(";");
        string declare;
          if(main_window_name==variable[1]){
            declare ="  public "+variable[0]+" "+variable[1]+" {get; set;}";
          }else
            declare ="  private "+variable[0]+" "+variable[1]+";";
          code_template.insert(wid_i+1,declare);
          
      }
    }
  }
  
  public void create_constructor(){
    int wid_i=code_template.index_of(CONSTRUCTOR);
    if(wid_i!=-1)
      code_template.insert(wid_i+1,"  public "+klass+"(){");
  }
  
  public void create_widgets(Gtk.ListStore widget_table){
    int wid_i=code_template.index_of(WIDGET_CREATION);
    if(wid_i!=-1){
      int[] widget_indices= init_widget_indices(widget_table);
      for(int i=0;i<widget_indices.length;i++){
        string[] variable=(widgets.get(widget_indices[i])).split(";");
        string declare;
        if(members)
          declare ="      "+variable[1]+" = builder.get_object(\""+variable[1]+"\") as "+variable[0]+";\n";
        else
          declare ="      var "+variable[1]+" = builder.get_object(\""+variable[1]+"\") as "+variable[0]+";\n";
        if(variable.length>2){
          for(int h=2;h<variable.length-1;h+=2){
            declare += "      "+variable[1]+"."+variable[h]+"+=("+variable[h+1]+");\n";          
          }
          
        }
        code_template.insert(wid_i+1,declare);
      }
    }
  }
  public void create_connect_destroy(string? main_window_name){
    if(main_window_name!=null){
      int wid_i=code_template.index_of(CONNECT_DESTROY);
      if(wid_i!=-1)
        code_template.insert(wid_i+1,"      %s.destroy.connect(Gtk.main_quit);".printf(main_window_name));
    }
  }
  public void create_handlers(Gtk.ListStore widget_table){
    
    int[] widget_indices= init_widget_indices(widget_table);
    for(int i=0;i<widget_indices.length;i++){
      string[] variable=(widgets.get(widget_indices[i])).split(";");
      if(variable.length>2){
        for(int h=2;h<variable.length-1;h+=2){
          string declare ="  [CCode (instance_pos = -1)]\n  private void "+variable[h+1]+"("+variable[0]+" source){\n  \n  }\n";
            
          handlers.add(declare);
          
        }
        
      }
      
    }
    
    Gee.Iterator<string> handler_it =handlers.iterator();
    int wid_i=code_template.index_of(SIGNAL_HANDLER);
    while(handler_it.has_next()){
      handler_it.next();
      code_template.insert(wid_i+1,handler_it.get());
    }
  }
  
  public void create_object_and_run(string? main_window_name){
    int wid_i=code_template.index_of(CREATE_OBJECT_AND_RUN);
    if(wid_i!=-1){
      code_template.insert(wid_i+1,"    %s %s =  new %s();".printf(klass, klass.down(), klass));
      if(main_window_name!=null)
        code_template.insert(wid_i+2,"    %s.%s.show_all ();".printf(klass.down(),main_window_name));
      
      if(namespace_set) code_template.add("}\n");
    }  
  }

  [CCode (instance_pos = -1)]
  public string generate_code(){
    handlers.clear();
    
     
    for(int i=0;i<code_template.size;i++){
      text.append(code_template.get(i));
      text.append("\n");
    }
    var tmp = text.str;
    text.erase();
    return tmp;
  }

}
}
